//
//  UserDefaultsCache.swift
//  
//
//  Created by tank on 2021/11/17.
//

import Foundation
import Logging

public class UserDefaultsCache: CacheProtocol {

    var logger = Logger(label: "LibBase.UserDefaultsCache")

    public let path: String?


    public required init(path: String?) {
        self.path = path
    }

    public func bool(key: String) -> Bool? {
        return UserDefaults.standard.bool(forKey: cacheKey(key: key))
    }

    public func int(key: String) -> Int? {
        return UserDefaults.standard.integer(forKey: cacheKey(key: key))
    }

    public func float(key: String) -> Float? {
        return UserDefaults.standard.float(forKey: cacheKey(key: key))
    }

    public func double(key: String) -> Double? {
        return UserDefaults.standard.double(forKey: cacheKey(key: key))
    }

    public func string(key: String) -> String? {
        return UserDefaults.standard.string(forKey: cacheKey(key: key))
    }
    
    public func data(key: String) -> Data? {
        return UserDefaults.standard.data(forKey: key)
    }

    public func object<T: Codable>(type: T.Type, key: String) -> T? {
        guard let data = UserDefaults.standard.data(forKey: cacheKey(key: key)) else {
            return nil
        }

        let decoder = CodableBuilder().createJSONDecoder()

        return try? decoder.decode(type, from: data)
    }

    public func objectNSSecureCoding<T: NSSecureCoding & NSObject>(type: T.Type, key: String) -> T? {
        guard let data = UserDefaults.standard.data(forKey: cacheKey(key: key)) else {
            return nil
        }

        if #available(iOS 11.0, *) {
            return try? NSKeyedUnarchiver.unarchivedObject(ofClasses: [NSString.self, NSDate.self, type], from: data) as? T
        } else {
            return NSKeyedUnarchiver.unarchiveObject(with: data) as? T
        }
    }

    public func setBool(value: Bool, key: String) {
        UserDefaults.standard.set(value, forKey: cacheKey(key: key))
    }

    public func setInt(value: Int, key: String) {
        UserDefaults.standard.set(value, forKey: cacheKey(key: key))
    }

    public func setFloat(value: Float, key: String) {
        UserDefaults.standard.set(value, forKey: cacheKey(key: key))
    }

    public func setDouble(value: Double, key: String) {
        UserDefaults.standard.set(value, forKey: cacheKey(key: key))
    }

    public func setString(value: String, key: String) {
        UserDefaults.standard.set(value, forKey: cacheKey(key: key))
    }
    
    public func setData(value: Data, key: String) {
        UserDefaults.standard.set(value, forKey: key)
    }

    public func setObject<T: Codable>(value: T, key: String) {
        let encode = CodableBuilder().createJSONEncoder()

        guard let data = try? encode.encode(value) else {
            return
        }

        UserDefaults.standard.set(data, forKey: cacheKey(key: key))
    }

    public func setObjectNSSecureCoding<T: NSSecureCoding>(value: T, key: String) {
        let data: Data?

        if #available(iOS 11.0, *) {
            do {
                data = try NSKeyedArchiver.archivedData(withRootObject: value, requiringSecureCoding: true)
            } catch  {
                data = nil
                logger.error("\(error.localizedDescription)")
            }

        } else {
            data = NSKeyedArchiver.archivedData(withRootObject: value)
        }

        if let data = data {
            UserDefaults.standard.set(data, forKey: cacheKey(key: key))
        }
    }

    public func remove(key: String) {
        UserDefaults.standard.removeObject(forKey: cacheKey(key: key))
    }

    public func removeAll() {
        UserDefaults.resetStandardUserDefaults()
    }
}
